之前我們用過 @Input()
、@Output()
,完成父子元件間的資料傳遞,但若今天兩個 Component 不是父子關係,這個方法可能就行不通了,今天會簡單介紹 RxJS 以及如何解決我們前篇遇到的問題。
RxJS 是一套非同步的函式庫,我們之前用的 HttpClient
,在使用 post()
、get()
這些 function 時,實際上是回傳 Observable
的物件,這個類別就是 RxJS 提供的。
先簡單介紹一些名詞:
Observable
可觀察物件,也就是被觀察的對象Observer
觀察者物件,會去觀察 Observable
物件。subscribe()
訂閱動作,可以想像成在 Observer
和 Observable
中建立連結。// http.service.ts
data$: Observable<any> = new Observable<any>();
login(data): Observable<any> {
this.data$ = this.http.post('/api/user/login', data);
return this.data$;
}
// login.component.ts
onSubmit() {
this.response$ = this.http.login(this.account);
this.response$.subscribe(
(result) => {
console.log(result);
}
);
}
這邊的 this.data$
就是一個 Observable
物件,return 給 login.component.ts
的 this.response$
。
然後 this.response$.subscribe()
這邊是做訂閱的動作,subscribe()
裡面可以放一個 Observer
物件,或是放入三個 callback function。而 Observer
其實就是將 next
、error
、complete
三個 function 包起來,所以其實沒甚麼差別。
subscribe(observer?: PartialObserver<T>): Subscription;
subscribe(next: null | undefined, error: null | undefined, complete: () => void):
我們用的是第二種,也就是直接傳一個 function,為第一個參數 next
,error 跟 complete 另有用途,不過不傳也沒關係。next
可以理解為,當 Observable
的物件發生變化時,會通知Observer
,Observer
會執行 next
裡面的事情。所以就會在收到資料後 用 console.log()
將資料印出。
next :
(result) => {
console.log(result);
}
所以我們現在知道一些事,在我們做訂閱這個動作後,Observable
發生改變,就會讓 Observer
做一些事。
那如果我今天在 LoginComponent
裡呼叫了 this.http.login()
,要怎麼讓 NavigationBarComponent
知道已經登入成功了呢?我們可以使用 Subject
物件。
我們在 HttpService
裡建立一個 Subject
物件,讓NavigationBarComponent
去訂閱它的變化。
接著,LoginComponent
呼叫 this.http.login()
,拿到結果後,用 next
方法,把剛拿到的結果塞給 this.http.sub
,NavigationBarComponent
就會偵測到變化,拿到剛剛 post()
的結果,改變 this.isLogin
的狀態。所以我們可以實現,在兩個非父子關係的元件用 Subject
傳遞訊息。
執行結果:
// http.service.ts
import { Observable, Subject } from 'rxjs';
sub = new Subject<any>();
// navigation-bar.component.ts
this.http.sub.subscribe((res) => {
this.isLogin = res.status;
console.log('nav: ' + res.status);
});
// login.component.ts
onSubmit() {
this.response$ = this.http.login(this.account);
this.response$.subscribe(
(result) => {
this.http.sub.next(result);
}
);
}